/**
********************************************************************************
@file     signal_linked_list.c
@brief    单向链表的实现
@details
本文件中简要介绍了单向链表的结构和用法。并且实现了对单向链表的操作的函数。
@author   CaiJing
@date     2021-05-11 08:48:27
@version  v0.2.5

\n Email:  wy1107412334@163.com
\n Blog:   None
\n Github: https://github.com/wy1107412334
\n Gitee:  https://gitee.com/git.git1107412334.com

********************************************************************************
@note

链表简介
=======

1. 本链表是一个单向链表。从链表头节点一直向后指，一直可以指到尾结点。
2. 本链表有一个链表表头和若干个链表节点。
  - 链表表头用于表示一个链表。其中保存了链表的头节点和尾节点的地址。
3. 空链表的表头的 head 和 tail 元素的值为 NULL。
4. 非空链表的尾节点的 next 元素的值为 NULL。


链表使用
=======
1. 数据类型 UT_SL_NODE 需要放在用户自定义的struct的最开头，
   这样链表节点的地址也就是用户自定义的struct的首地址。
   这样方便通过本链表返回的节点地址来处理用户自定义的数据:\n
   @code
   struct abc {
       UT_SL_NODE node;
       // 用户定义的数据类型字段
   };
   @endcode
2. 先创建并初始化一个链表的表头，可以使用如下方式:
  - 定义 UT_SL_LIST 类型变量，然后将变量的地址传递给函数 ut_sllist_static_init()
3. 对上面初始化的链表进行操作。

********************************************************************************
@attention
None.
********************************************************************************
@verbatim

=====================================
        单向链表的内存布局图
=====================================

空链表
=====

链表头:
+------+------+
| head | NULL |
+------+------+
| tail | NULL |
+------+------+


存在一个节点的链表
=================

链表头:                节点
+------+         +---->+------+------+
| head |---------+     | next | NULL |
+------+         |     +------+------+
| tail |---------+
+------+


存在两个节点的链表
=================

链表头:                节点1                节点2
+------+         +---> +------+      +----> +------+------+
| head |---------+     | next |------+      | next | NULL |
+------+               +------+      |      +------+------+
| tail |-----------------------------+
+------+


存在三个节点的链表
=================

链表头:                节点1          节点2             节点3
+------+         +---> +------+  +--> +----- +   +----> +------+------+
| head |---------+     | next |--+    | next |---+      | next | NULL |
+------+               +------+       +------+   |      +------+------+
| tail |-----------------------------------------+
+------+


使用链表节点的数据类型
=====================

struct abc {
    UT_SL_NODE node;
    int        val;
};

链表头:                节点1          节点2             节点3
+------+         +---> +------+  +--> +----- +   +----> +------+------+
| head |---------+     | next |--+    | next |---+      | next | NULL |
+------+               +------+       +------+   |      +------+------+
| tail |---------+     | val  |       | val  |   |      | val  |
+------+         |     +------+       +------+   |      +------+
                 +-------------------------------+

@endverbatim
********************************************************************************
@par 修订记录
- jcai, 2024-09-08
  - 从 data_structure 工程中 signal_link_list.c 移动而来
  - 移除一些使用不到的函数
  - 每个函数名增加前缀 ut_
- jcai, 2024-09-08
  - 移除函数sllist_traverse()
- jcai, 2024-09-07
  - 完善函数 sllist_insert_at() 的功能
- jcai, 2024-09-07
  - 将支持的最低编译版本提高到C99
- jcai, 2024-09-07
  - 将头文件 bash_data_type.h 修改为 base_data_types.h
- jcai, 2024-09-06
  - 使用表达式校验宏来校验函数入参
  - 修改了一些函数的原型与注释
- jcai, 2024-09-04
  - 修改注释格式
- jcai, 2024-09-03
  - 当定义了宏 DEBUG 时，才在函数 sllist_insert_head()/sllist_insert_tail()/
    sllist_insert_at() 中运行判断要添加的节点是否已经存在的功能
- jcai, 2024-09-03
  - 重命名 sllist_destroy() 为 sllist_cleanup(), 并修改内部实现逻辑
- jcai, 2024-09-03
  - 增加宏 ALLOC_EN 来使能需要malloc()/free()支持的功能
  - 修改注释说明
- jcai, 2023-07-05 13:24
  - 修改宏 SL_FOREACH 的用法
- jcai, 2021年8月3日 10点24分
  - 完善doxygen注释格式
- jcai, 2021年7月9日 14点02分
  - 修改统计链表长度的方式
  - 修改doxygen注释格式
- jcai, 2021年6月29日 02点02分
  - 新增销毁链表的函数
  - 修复注释问题
- jcai, 2021年5月24日 19点13分
  - 增加函数 sllist_deleteAt(), sllist_inversion()
  - 使用枚举值替代函数返回的错误码
- jcai, 2021年5月19日 19点07分
  - 修改寻找链表中指定节点的函数及其回调函数的参数表
- jcai, 2021年5月18日 20点48分
  - 修改文件头注释
- jcai, 2021年5月18日 18点32分
  - 优化排序函数
- jcai, 2021年5月17日 09点17分
  - 修复链表排序函数丢失节点的错误
  - 新增从链表指定节点之后插入节点的函数
  - 新增以节点首地址转换为节点索引的函数
  - 删除行末多余空白字符
  - 删除多余头文件
- jcai, 2021年5月16日 21点32分
  - 新增清空链表的函数
- caijing，2021-05-11 08:48:27
  - 首次编写

<center>Copyright &copy; 2021, 2023-2024 wy1107412334@163.com All rights reserved</center>
********************************************************************************
*/

/* 头文件导入区 ------------------------------------------------------------- */

#include "signal_linked_list.h"



/** @defgroup SIGNAL_LINKED_LIST SIGNAL_LINKED_LIST
 * @brief 单向链表模块
 * @{
 */



/* 宏定义区 ----------------------------------------------------------------- */






/* 数据类型定义区 ----------------------------------------------------------- */








/* 变量定义区 --------------------------------------------------------------- */











/* 函数定义区 --------------------------------------------------------------- */





/**
********************************************************************************
@brief   清空链表
@details
当 callback 不为 NULL 的时候，会使用回调函数 callback 来遍历链表
sl 中的所有节点。
清空成功之后，链表 sl 就是一个空链表了
@param [in] sl: 指向链表头的指针
@param [in] callback: 一个函数的指针。可以为 NULL
@return none
*/
void ut_sllist_cleanup(UT_SL_LIST *sl, ut_sl_trav_callback callback)
{
    if (callback != NULL)
    {
        for (UT_SL_NODE * tmp, * pNode = sl->head; pNode; pNode = tmp)
        {
            tmp = pNode->next;
            callback(pNode);
        }
    }
    UT_SLLIST_INIT(sl);
}

/**
********************************************************************************
@brief   判断链表是否为空
@details 判断链表是否为空
@param [in] sl: 指向链表头的指针
@retval  1: 参数 sl 指向的链表为空
@retval  0: 参数 sl 指向的链表为非空
@note    None.
*/
uint32_t ut_sllist_isempty(const UT_SL_LIST *sl)
{
    if (sl->head)   return 0;

    return 1;
}

/**
********************************************************************************
@brief   获得链表的长度
@details 返回链表中节点的数量
@param [in] sl: 指向链表头的指针
@return  返回链表中节点的数量
@note    None.
*/
uint32_t ut_sllist_length(const UT_SL_LIST *sl)
{
    return sl->length;
}

/**
********************************************************************************
@brief   从链表头插入一个节点
@details 从链表头插入一个节点
@param [in] sl: 指向链表头的指针
@param [in] node: 指向一个节点的指针
@retval  0: 插入节点成功
@note    这个新插入的节点 node 就是链表 sl 的头节点。
@see sllist_insert_tail()
*/
int32_t ut_sllist_insert_head(UT_SL_LIST *sl, UT_SL_NODE *node)
{
    node->next = sl->head;
    sl->head = node;
    sl->length++;
    if (!sl->tail)
        sl->tail = node;

    return 0;
}

/**
********************************************************************************
@brief   从链表尾插入一个节点
@details 从链表尾插入一个节点
@param [in] sl: 指向链表头的指针
@param [in] node: 指向一个节点的指针
@retval  0: 插入节点成功
@note    这个新插入的节点 node 就是链表 sl 的尾节点。
@see sllist_insert_head()
*/
int32_t ut_sllist_insert_tail(UT_SL_LIST *sl, UT_SL_NODE *node)
{
    node->next = NULL;
    sl->length++;
    if (sl->tail)
    {
        sl->tail->next = node;
        sl->tail       = node;
    }
    else
    {
        sl->head = node;
        sl->tail = node;
    }

    return 0;
}

/**
********************************************************************************
@brief   寻找链表中的节点
@details 调用回调函数 callback 来遍历链表 sl 中的节点；
         如果找到了节点，那么 callback 返回非0，
         本函数就会返回该节点的地址。
@param [in] sl: 指向链表头的指针
@param [in] callback: 用于寻找节点的函数。如果找到了就返回非0值；否则返回0
@param [in] arg: 用于回调函数 callback 的第二个参数
@return
- 寻找到了节点返回该节点的地址
- 没有找到节点则返回 NULL
@note
本函数用于遍历链表 sl 中的节点，
回调函数 callback 用于指定是否找到要寻找的节点。
*/
UT_SL_NODE *ut_sllist_find(const UT_SL_LIST *sl, ut_sl_match_callback callback, void *arg)
{
    UT_SL_FOREACH(pNode, sl)
    {
        if (callback(pNode, arg))   return (UT_SL_NODE *)pNode;
    }

    return NULL;
}

/**
********************************************************************************
@brief   删除链表中节点
@details 删除链表 sl 中节点 node
@param [in] sl: 指向链表头的指针
@param [in] node: 指向节点的指针
@retval UT_SL_ERR_ISEMPTY: 链表 sl 为空链表
@retval UT_SL_ERR_NOTEXIST: 链表 sl 中没有找到节点 node
@retval 0: 成功
@note    none
@see ut_sllist_at()
*/
int32_t ut_sllist_delete(UT_SL_LIST *sl, const UT_SL_NODE *node)
{
    int32_t ret = UT_SL_ERR_NOTEMPTY;
    if (!sl->head)      goto return_point;

    ret = UT_SL_ERR_NOTEXIST;
    UT_SL_NODE * pNode = sl->head;
    if (pNode == node)
    {
        if (sl->head == sl->tail)
            sl->tail = sl->tail->next;
        sl->head = sl->head->next;
        ret = 0;
        sl->length--;
        goto return_point;
    }

    for (; pNode; pNode=pNode->next)
    {
        if (pNode->next == node)
        {
            if (pNode->next == sl->tail)
                sl->tail = pNode;
            pNode->next = pNode->next->next;
            ret = 0;
            sl->length--;
            goto return_point;
        }
    }

return_point:
    return ret;
}

/**
********************************************************************************
@brief   获得指定节点地址
@details 获得链表 sl 中第 index 个节点的地址。
@param [in] sl: 指向链表头的指针
@param [in] index: 第几个节点。有效值范围: 0 ~ 链表sl中节点总数 - 1
@return  成功则返回节点地址；否则返回NULL
@note    none
*/
UT_SL_NODE *ut_sllist_at(const UT_SL_LIST *sl, uint32_t index)
{
    uint32_t       i = 0;
    for (UT_SL_NODE * pNode = sl->head; pNode != NULL; pNode = pNode->next)
    {
        if (i++ == index)
            return pNode;
    }
    return NULL;
}




/** defgroup SIGNAL_LINKED_LIST
 * @}
 */


/*********************************** END OF FILE ******************************/
